//
//  Utils.swift
//
//  Created by IOS-User on 2017/4/1.
//  Copyright © 2017年 IOS-User. All rights reserved.
//

/**!
 * 常用工具类
 */
import Foundation
import Photos
import UIKit
import YYKit
import LocalAuthentication

struct Utils {
    
    //缓存目录
    private static let K_APPLICATION_CACHE_PATH = String.init(format: "%@/%@", NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last!,Setting.K_APP_BUNDIE_IDENTIFIER as! CVarArg)
    
    /** 压缩包缓存路径 */
    static let K_APP_ZIP_CACHE_PATH = String.init(format: "%@/SSZipArchive", NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last!)
    
    //随机字符串
    private static let K_APP_RANDOM_ARRAY = [
        "a", "b", "c", "d", "e", "f", "g","h", "i", "j", "k", "m", "n","o", "p",
        "q", "r", "s", "t","u", "v","w", "x", "y", "z","A", "B", "C", "D", "E", "F",
        "G","H", "I", "J", "K", "L", "M", "N","O", "P", "Q", "R","S","T","U", "V", "W",
        "X", "Y", "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    
    let setBoolkeys:Set<String> = ["quoteTotalStrategy","quoteHermeticStrategy","quoteMultiplePurchases","quoteEditLimit","submit","quoteMultipePurchases"]
    
    /** 阴影方向 */
    enum ShadowDirection:Int {
        case left = 1
        case top = 2
        case right = 3
        case bottom = 4
    }
    
    private static let _shareInstance = Utils.init()
    static func shareInstance() -> Utils {
        return _shareInstance
    }
    
    
    //MARK: - 相关动画设置
    /**! tabbar侧滑动画  */
    func tabBarChangeAnimation(tabBarController:UITabBarController,
                               withDirection direction:Int){
        
        let transition:CATransition = CATransition.init()
        transition.duration = 0.2
        transition.type = CATransitionType.moveIn//kCATransitionReveal
        
        if(direction == 0) {
            transition.subtype = CATransitionSubtype.fromLeft
        }
        else{
            transition.subtype = CATransitionSubtype.fromRight
        }
        
        //transition.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.default)
        
        tabBarController.view.layer.add(transition, forKey: "reveal")//switchView
    }
    
    /**!
     * 视图动画(分享)
     * @para viewItems [UIView] 需要显示动画的视图
     * @para _h        CGFloat  视图高度
     * @columns        Int 每行显示的栏目数(默认每行显示 4个)
     */
    func startAnimationsCompletion(viewItems:[UIView],
                                   _h:CGFloat,
                                   columns:Int = 4) {
        
        var _index:Int = 0
        var _y:CGFloat = _h
        for _view in viewItems {
            _view.alpha = 0
            _y = CGFloat((_index % columns) + 1) * _h
            _view.transform = CGAffineTransform.init(translationX: 0, y: _y)
            
            UIView.animate(withDuration: 0.85,
                           delay: Double.init(_index) * 0.035,
                           usingSpringWithDamping: 0.6,
                           initialSpringVelocity: 0,
                           options: UIView.AnimationOptions.curveLinear,
                           animations: {
                _view.alpha = 1
                _view.transform = CGAffineTransform.identity
            },
                           completion: nil)
            
            _index += 1
        }
    }
    
    
    /**!
     * 翻转动画
     * @para tagView UIView 执行动画的视图
     * @para isBack 动画方向 默认向右
     * @para animationCallBack 回调事件
     */
    func animationOverturn(tagView:UIView,
                           isBack:Bool = false,
                           animationCallBack:(()->())?) {
        UIView.transition(with: tagView,
                          duration: 0.35,
                          options: isBack ? .transitionFlipFromRight : .transitionFlipFromLeft,
                          animations: {
            animationCallBack!()
        }, completion:{(finished:Bool) in
            
            if finished, let _imgView:UIImageView = tagView.viewWithTag(666) as? UIImageView {
                UIView.animate(withDuration: 0.25, animations: {
                    _imgView.transform = CGAffineTransform(scaleX: 1.65, y: 1.65)
                }) { (_:Bool) in
                    UIView.animate(withDuration: 0.25, animations: {
                        _imgView.transform = CGAffineTransform(scaleX: 1, y: 1)
                    })
                }
            }
        })
    }
    
    
    //MARK: - 时间相关方法
    /**! 获取本地字符串时间戳 */
    func localUnixTime() -> String {
        let time:TimeInterval = Date.init().timeIntervalSince1970
        let timeStamp = UInt32(time)
        return String.init(format: "%lD", timeStamp)
    }
    
    /**!
     * 获取当前时间
     * @param m String 时间格式
     */
    func localDateTime(Formatter m:String) -> String {
        let date = Date.init()
        
        let dateFormat:DateFormatter = DateFormatter.init()
        dateFormat.dateFormat = m
        
        return dateFormat.string(from: date)
    }
    
    /**! 获取本地整数时间戳 */
    func localUnixTimeForInt() -> TimeInterval {
        let time:TimeInterval = Date.init().timeIntervalSince1970
        //let timeStamp = UInt32(time)
        return time
    }
    
    /**!
     * 根据字符串时间获取 Date
     * @para strDateTime String   字符串时间
     * @para type Int 时间格式(0：yyyy-MM-dd 1: yyyy/MM/dd 2: yyyy-MM-dd HH:mm:ss)
     */
    func getDateForStringDate(strDateTime:String,
                              convertType type:Int = 0) -> Date? {
        var date:Date?
        
        let formatter:DateFormatter = DateFormatter.init()
        switch type {
        case 1:
            formatter.dateFormat = "yyyy/MM/dd"
            
        case 2:
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
            
        default:
            formatter.dateFormat = "yyyy-MM-dd"
        }
        
        date = formatter.date(from: strDateTime)
        
        return date
    }
    
    /**!
     * 根据字符串时间获取 Date
     * @para strDateTime String   字符串时间
     * @para type Int 时间格式
     */
    func getUnixtimeForStringDate(strDateTime:String,
                                  convertType type:Int = 0) -> TimeInterval {
        
        let formatter:DateFormatter = DateFormatter.init()
        switch type {
        case 1:
            formatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
        case 2:
            formatter.dateFormat = "yyyy-MM-dd"
            
        default:
            formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
        }
        
        let date:Date = formatter.date(from: strDateTime)!
        
        let time:TimeInterval = date.timeIntervalSince1970
        return time
    }
    
    /**!
     * 根据Unixtime 时间戳 获取倒计时的时间
     * @para unixTime UInt32
     */
    func getTimerDateForTimeinterval(timeInterval:UInt32) -> String {
        var _strTime:String = ""
        
        //86400:一天的秒数
        if timeInterval > 86400 {
            _strTime = String.init(format:"%lD天%lD时%lD分",timeInterval / 86400,(timeInterval / 86400 / 3600),timeInterval / 86400 / 3600 % 3600)
        }
        //3600:一小时的秒数
        else if timeInterval > 3600 {
            _strTime = String.init(format:"%lD时%lD分%lD秒",timeInterval / 3600,timeInterval / 60 % 60,timeInterval % 60)
        }
        else{
            _strTime = String.init(format:"%lD分%lD秒", timeInterval / 60 % 60,timeInterval % 60)
        }
        
        return _strTime
    }
    
    /**!
     * 根据Unixtime 时间戳 获取倒计时的时间
     * @para unixTime UInt32
     */
    func getTimerDateFor(StringDate sd:String,
                         andFormaat fm:String = "yyyy-MM-dd HH:mm:ss") -> String {
        
        //%(truncatingRemainder) 模运算
        let timeInterval:TimeInterval = self.getUnixtimeForStringDate(strDateTime: sd) - self.localUnixTimeForInt()
        var _strTime:String = ""
        
        //86400:一天的秒数
        if timeInterval > 86400 {
            //_strTime = String.init(format:"%lD天%lD时",timeInterval / 86400,(timeInterval / 86400 / 3600))
            _strTime = String.init(format:"%.f天%.f时",floor(timeInterval / 86400),floor(timeInterval.truncatingRemainder(dividingBy:86400)/3600))
        }
        //3600:一小时的秒数
        else {
            //_strTime = String.init(format:"%lD时%lD分",timeInterval / 3600,timeInterval / 60 % 60)
            _strTime = String.init(format:"%.f时%.f分",floor(timeInterval / 3600),(timeInterval / 60).truncatingRemainder(dividingBy: 60))
        }
        
        return _strTime
    }
    
    /**!
     * 将Unix 时间戳转换为 字符串时间
     * @para unixTime  UInt32
     * @para strFormat String 转换格式
     */
    func getDateTimeForUnix(unixTime:UInt32,
                            strFormat:String) -> String {
        
        let date:Date? = Date.init(timeIntervalSince1970: TimeInterval(unixTime))
        
        let dateFormat:DateFormatter = DateFormatter.init()
        dateFormat.dateFormat = strFormat
        
        return dateFormat.string(from: date!)
    }
    
    
    /// 获取时间
    /// - Parameters:
    ///   - d: Date
    ///   - strFormat: 时间格式化
    func getStringDateTimeFor(Date d:Date,
                              strFormat:String) -> String {
        
        let dateFormat:DateFormatter = DateFormatter.init()
        dateFormat.dateFormat = strFormat
        
        return dateFormat.string(from: d)
    }
    
    
    //MARK: - 获取设备UUID
    /**!
     * 获取当前设备的UUID
     */
    func getCurrentDeviceUUID() -> String {
        let uuid:UUID = UIDevice.current.identifierForVendor!
        return uuid.uuidString
    }
    
    /**! 开启设置权限 */
    func openSetting(){
        let strUR = UIApplication.openSettingsURLString
        
        if UIApplication.shared.canOpenURL(URL.init(string: strUR)!) {
            UIApplication.shared.open(URL.init(string: strUR)!, options: [:], completionHandler: nil)
        }
    }
    
    
    /// 判断当前设备是否支持TouchID
    /// - Returns: true 支持
    func isSupportTouchID() -> Bool {
        //https://www.jianshu.com/p/9990b0f48488
        let context:LAContext = LAContext.init()
        context.localizedFallbackTitle = "输入密码"
        
        var _error:NSError?
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error:&_error) {
            //释放
            context.invalidate()
            return true
        }
        
        //释放
        context.invalidate()
        return false
    }
    
    
    //MARK: - 生成随机数
    /**! 生成指定范围内的帧数随机数  */
    private func randomIn(min: Int, max: Int) -> UInt {
        return UInt(arc4random()) % UInt(max - min + 1) + UInt(min)
    }
    
    /**! 生成指定长度的字符串随机数 */
    func randomString(len:Int) -> String {
        var _r:String = ""
        for _ in 0..<len {
            let index = Int(randomIn(min: 0, max: Utils.K_APP_RANDOM_ARRAY.count - 1))
            if Utils.K_APP_RANDOM_ARRAY.count > index {
                _r += Utils.K_APP_RANDOM_ARRAY[index]
            }
        }
        return _r
    }
    
    
    //MARK: - 缓存相关设置
    /**! 获取系统缓存 */
    func getAppCacheSize() -> String {
        
        var strResult = "0M"
        
        let fileManage = FileManager.default
        var isDirectory:ObjCBool = ObjCBool.init(false)
        
        //是否存在
        if fileManage.fileExists(atPath: Utils.K_APPLICATION_CACHE_PATH, isDirectory:&isDirectory) {
            
            do {
                var size = Float.init(0)
                var attributDic:[FileAttributeKey : Any]?
                
                //文件夹
                if isDirectory.boolValue {
                    
                    var tempPath:String?
                    
                    //获取该目录下所有文件
                    for strItem in fileManage.subpaths(atPath: Utils.K_APPLICATION_CACHE_PATH)! {
                        tempPath = Utils.K_APPLICATION_CACHE_PATH + "/" + strItem
                        
                        //存在，且为文件
                        if fileManage.fileExists(atPath: tempPath!, isDirectory: &isDirectory),isDirectory.boolValue == false{
                            attributDic = try fileManage.attributesOfItem(atPath: tempPath!)
                            size = size + (attributDic?[FileAttributeKey.size] as? Float)!
                        }
                    }
                }
                else{
                    attributDic = try fileManage.attributesOfItem(atPath: Utils.K_APPLICATION_CACHE_PATH)
                    size = (attributDic?[FileAttributeKey.size] as? Float)!
                }
                
                //计算缓存大小
                size = size / (1024 * 1024)
                if size > 0 {
                    strResult = String.init(format:"%.2fM",size)
                }
            }
            catch {
                print("异常：\(error)")
            }
        }
        
        return strResult
    }
    
    /**!
     * 清理系统缓存
     */
    func clearnAppCache() -> Bool {
        var isOk = false
        
        let fileManage = FileManager.default
        var isDic:ObjCBool = ObjCBool.init(false)
        
        //路径存在，且为文件夹
        if fileManage.fileExists(atPath: Utils.K_APPLICATION_CACHE_PATH, isDirectory: &isDic),isDic.boolValue == true {
            
            do {
                for var strItem in fileManage.subpaths(atPath: Utils.K_APPLICATION_CACHE_PATH)! {
                    strItem = Utils.K_APPLICATION_CACHE_PATH + "/" + strItem
                    
                    //移除
                    if fileManage.fileExists(atPath: strItem) {
                        try fileManage.removeItem(atPath: strItem)
                    }
                }
                
                isOk = true
            } catch  {
                print("异常：\(error)")
            }
        }
        
        return isOk
    }
    
    
    //MARK: - 信息验证
    /**!
     * 验证手机号、座机号
     * @return true 通过验证
     */
    func checkPhoneNo(strPhoneNo:String?) -> Bool {
        
        if strPhoneNo == nil || (strPhoneNo?.isEmpty)! {
            return false
        }
        
        let strRegex = "^\\d{3,4}[-]?\\d{7,8}$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        
        return predicate.evaluate(with: strPhoneNo!)
    }
    
    /**!
     * 验证邮箱
     * @return true 通过验证
     */
    func checkEmail(strEmail:String?) -> Bool {
        
        if strEmail == nil || (strEmail?.isEmpty)! {
            return false
        }
        
        let strRegex = "^[A-Z0-9a-z_\\.\\-]+\\@([A-Za-z0-9\\-]+\\.)+([A-Za-z0-9])+$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        
        return predicate.evaluate(with: strEmail!)
    }
    
    
    /**!
     * 验证密码(密码为6~12位的数字字母)
     * @return true 通过验证
     */
    func checkPassword(strPwd:String?) -> Bool {
        
        if strPwd == nil || (strPwd?.isEmpty)! {
            return false
        }
        
        //长度限制
        let length = strPwd?.lengthOfBytes(using: String.Encoding.utf8)
        if length! < 6 || length! > 12 {
            return false
        }
        
        //是否含有Emoji 表情
        if stringContainsEmoji(string: strPwd!){
            return false
        }
        
        let strRegex = "^[A-Za-z0-9 ]{6,12}$"
        let predicate = NSPredicate.init(format:"SELF MATCHES %@",strRegex)
        return predicate.evaluate(with: strPwd!)
    }
    
    /**!
     * 验证是否为字母
     * @return true 通过验证
     */
    func checkAlphabet(strInfo:String?) -> Bool {
        
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        let strRegex = "^[A-Za-z]{1,}$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        
        return predicate.evaluate(with: strInfo!)
    }
    
    /**!
     * 验证是否为物流单号
     * @return true 通过验证
     */
    func checkWaybillnumberFor(Conpany cn:String,
                               AndNumber strInfo:String?) -> Bool {
        
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        let _path = Bundle.main.path(forScaledResource: "wuliu_regex", ofType: "json")
        let _data:Data = try! NSData.init(contentsOfFile: _path!) as Data
        let jsonData = try? JSONSerialization.jsonObject(with: _data, options: [])
        
        var strRegex:String?
        if let arr:[[String:Any]] = jsonData as? [[String:Any]] {
            let tmpDic:[String:Any]? = arr.first(where: { "\($0["name"] ?? "")".contains(cn) || "\($0["name"] ?? "")".hasPrefix(cn.prefix(2)) })
            
            strRegex = tmpDic?["reg_mail_no"] as? String
        }
        
        strRegex = strRegex ?? "^[A-Za-z0-9]{1,}$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex!)
        return predicate.evaluate(with: strInfo!)
    }
    
    /**!
     * 验证是否为数字
     * @return true 通过验证
     */
    func checkNumber(strInfo:String?) -> Bool {
        
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        //+ 一次或多次，？0次或一次，* 零次或多次
        let strRegex = "^[0-9]+(\\.?)([0-9]*)$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        
        return predicate.evaluate(with: strInfo!)
    }
    
    
    /**! 检查银行卡号
     * @return true 通过验证
     */
    func checkBankNO(strInfo:String?) -> Bool {
        
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        let strRegex = "^([1-9]{1})(\\d{15}|\\d{18})$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        
        return predicate.evaluate(with: strInfo!)
    }
    
    /**! 税号验证
     * @return true 通过验证
     */
    func checkTaxNo(strInfo:String?) -> Bool {
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        let strRegex = "^[A-Z0-9]{15}$|^[A-Z0-9]{17}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        return predicate.evaluate(with: strInfo!)
    }
    
    /**! 验证货币
     * @return true 通过验证
     */
    func checkMoney(strInfo:String?) -> Bool {
        
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        let strRegex = "^\\d+(\\.\\d{1,4})?$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        return predicate.evaluate(with: strInfo!)
    }
    
    /**! 验证邮编
     * @return true 通过验证
     */
    func checkPostcode(strInfo:String?) -> Bool {
        
        if strInfo == nil || strInfo!.isEmpty {
            return false
        }
        
        let strRegex = "^[1-9][0-9]{5}$"
        let predicate = NSPredicate.init(format: "SELF MATCHES %@", strRegex)
        return predicate.evaluate(with: strInfo!)
    }
    
    
    /** 获取具体类型 */
    func getValueFor<T>(Any _any:NSObject?,
                        andCode code:String) -> T {
        
        //print("code:\(code),any:\(_any ?? "" as NSObject)")
        
        var any:Any?
        let arrCodes = code.components(separatedBy: ".")
        if arrCodes.count > 1 {
            
            var tmpObj:NSObject?
            for _code in arrCodes {
                if _code != arrCodes.last {
                    if tmpObj != nil {
                        if let _arr = tmpObj?.value(forKey: _code) as? NSArray {
                            tmpObj = _arr.firstObject as? NSObject
                        }
                        else{
                            tmpObj = tmpObj?.value(forKey: _code) as? NSObject
                        }
                    }
                    else{
                        if let _arr = _any?.value(forKey: _code) as? NSArray {
                            tmpObj = _arr.firstObject as? NSObject
                        }
                        else{
                            tmpObj = _any?.value(forKey: _code) as? NSObject
                        }
                    }
                }
                else{
                    any = tmpObj?.value(forKey: _code)
                }
            }
        }
        else{
            any = _any?.value(forKey: code)
        }
        
        if code.contains("quoteMultiplePurchases") || code.contains("quoteMultipePurchases") {
            return (any as? Bool ?? false) as! T
        }
        else if code.contains("quoteHermeticStrategy"){
            //return (any as? Bool ?? false) as! T
            return (any as? Bool == true ? "1":"0") as! T
        }
        else if code.contains("quoteEditLimit") {
            return (any as? Bool == true ? "1":"0") as! T
        }
        else if code.contains("quoteTotalStrategy") {
            return (any as? Bool ?? false) as! T
        }
        else if code.contains("mallSales") || code.contains("saled") {
            return (any as? Bool ?? false) as! T
        }
        
        //交集存在
        let setCodes = Set.init(arrCodes)
        if any is Bool && setBoolkeys.intersection(setCodes).count > 0 {
            return any as? Bool as! T
        }
        
        switch any {
            
        case is String:
            return any as! String as! T
            
        case is Double:
            if code.lowercased().contains("id") {
                return String.init(format:"%.0f",any as! Double) as! T
            }
            else if code.lowercased().contains("num") || code.lowercased().contains("delivery") {
                return String.init(format:"%.0f",any as! Double) as! T
            }
            else if code.lowercased().contains("taxrate") {
                return String.init(format:"%.2f",any as! Double) as! T
            }
            else if code.lowercased().contains("accountperiod") {
                return String.init(format:"%.0f",any as! Double) as! T
            }
            return String.init(format:"%.4f",any as! Double) as! T
            
        case is Float:
            if code.lowercased().contains("id") {
                return String.init(format:"%.0f",any as! Float) as! T
            }
            else if code.lowercased().contains("num") || code.lowercased().contains("delivery") {
                return String.init(format:"%.1f",any as! Float) as! T
            }
            else if code.lowercased().contains("taxrate") {
                return String.init(format:"%.2f",any as! Float) as! T
            }
            return String.init(format:"%.4f",any as! Float) as! T
            
        case is NSInteger:
            return String.init(format:"%.1f",any as! NSInteger) as! T
            
        case is Bool:
            return any as? Bool as! T
            
        default:
            print("未知：\(any ?? "")")
            return "" as! T
        }
    }
    
    
    //MARK: - Emoji表情检测
    /**!
     * 是否含有Emoji 表情(true 含有)
     */
    func stringContainsEmoji(string:String)->Bool{
        var returnValue = false;
        let Str = NSString(format: "%@", string);
        
        Str.enumerateSubstrings(in: NSMakeRange(0, Str.length), options: NSString.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) in
            
            let subStr = NSString(format: "%@", substring!);
            let hs = subStr.character(at: 0);
            // surrogate pair
            if (0xd800 <= hs && hs <= 0xdbff) {
                if (subStr.length > 1) {
                    let ls = subStr.character(at: 1);
                    let uc = Int(((hs - 0xd800) * 0x400) + (ls - 0xdc00)) + 0x10000;
                    if (0x1d000 <= uc && uc <= 0x1f77f){
                        returnValue = true;
                    }
                }
            }
            else if (subStr.length > 1) {
                let ls = subStr.character(at: 1);
                if (ls == 0x20e3){
                    returnValue = true;
                }
            }
            else {
                // non surrogate
                if (0x2100 <= hs && hs <= 0x27ff){
                    returnValue = true;
                }
                else if (0x2B05 <= hs && hs <= 0x2b07){
                    returnValue = true;
                }
                else if (0x2934 <= hs && hs <= 0x2935){
                    returnValue = true;
                }
                else if (0x3297 <= hs && hs <= 0x3299){
                    returnValue = true;
                }
                else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50){
                    returnValue = true;
                }
            }
        };
        
        return returnValue;
    }
    
    
    //MARK: - Json 与 字典、数组类型转换
    /**!
     * 字典转换为JSONString
     * @parameter dictionary: 字典参数
     * @returns: JSONString
     */
    func getJSONStringFromDictionary(dictionary:NSDictionary) -> String {
        if (!JSONSerialization.isValidJSONObject(dictionary)) {
            print("无法解析出JSONString")
            return ""
        }
        let data : NSData! = try! JSONSerialization.data(withJSONObject: dictionary, options: []) as NSData?
        let JSONString = NSString(data:data as Data,encoding: String.Encoding.utf8.rawValue)
        return JSONString! as String
    }
    
    /**!
     * 数组转换为JSONString
     * @parameter array: 数组
     * @returns: JSONString
     */
    func getJSONStringFromArray(array:[[String:Any]]) -> String {
        if (!JSONSerialization.isValidJSONObject(array)) {
            print("无法解析出JSONString")
            return ""
        }
        let data : NSData! = try! JSONSerialization.data(withJSONObject: array, options: []) as NSData?
        let JSONString = NSString(data:data as Data,encoding: String.Encoding.utf8.rawValue)
        return JSONString! as String
    }
    
    
    /// 获取Data
    /// - Parameter _any: Any
    func getJsonDataFor(Any _any:Any) -> Data? {
        
        if !JSONSerialization.isValidJSONObject(_any) {
            print("无法解析出JSONString")
            return nil
        }
        
        let dataTemp:Data? = try? JSONSerialization.data(withJSONObject: _any, options: [])
        return dataTemp
    }
    
    
    //MARK: - 设置富文本
    /**! 设置富文本 */
    func setAttributeStringText(strFullText:String,
                                andFullTextFont textFont:UIFont,
                                andFullTextColor textColor:UIColor,
                                withChangeText changeText:String,
                                withChangeFont changFont:UIFont,
                                withChangeColor changeColor:UIColor,
                                isLineThrough:Bool = false) -> NSAttributedString{
        
        var dicAttr:[NSAttributedString.Key:Any]?
        let attributeString = NSMutableAttributedString.init(string: strFullText)
        
        //不需要改变的文本
        var range = NSString.init(string: strFullText).rangeOfAll()
        
        dicAttr = [
            NSAttributedString.Key.font:textFont,
            NSAttributedString.Key.foregroundColor:textColor,
        ]
        attributeString.addAttributes(dicAttr!, range: range)
        
        //需要改变的文本
        range = NSString.init(string: strFullText).range(of: changeText)
        
        dicAttr = [
            NSAttributedString.Key.font:changFont,
            NSAttributedString.Key.foregroundColor:changeColor,
        ]
        
        if isLineThrough {
            dicAttr?[NSAttributedString.Key.strikethroughStyle] = NSNumber.init(value: 1)
        }
        attributeString.addAttributes(dicAttr!, range: range)
        
        return attributeString
    }
    
    func setAttributeStringTexts(strFullText:String,
                                 andFullTextFont textFont:UIFont,
                                 andFullTextColor textColor:UIColor,
                                 withChangeText changeTexts:[String],
                                 withChangeFont changFont:UIFont,
                                 withChangeColor changeColor:UIColor,
                                 isLineThrough:Bool = false) -> NSAttributedString{
        
        var range:NSRange?
        var dicAttr:[NSAttributedString.Key:Any]?
        let attributeString = NSMutableAttributedString.init(string: strFullText)
        
        //不需要改变的文本
        range = NSString.init(string: strFullText).range(of: String.init(strFullText))
        
        dicAttr = [
            NSAttributedString.Key.font:textFont,
            NSAttributedString.Key.foregroundColor:textColor,
        ]
        attributeString.addAttributes(dicAttr!, range: range!)
        
        //需要改变的文本
        for item in changeTexts {
            range = NSString.init(string: strFullText).range(of: item)
            
            dicAttr = [
                NSAttributedString.Key.font:changFont,
                NSAttributedString.Key.foregroundColor:changeColor,
            ]
            
            if isLineThrough {
                dicAttr?[NSAttributedString.Key.strikethroughStyle] = NSNumber.init(value: 1)
            }
            attributeString.addAttributes(dicAttr!, range: range!)
        }
        
        return attributeString
    }
    
    func setAttributeStringTexts(strFullText:String,
                                 andFullTextFont textFont:UIFont,
                                 andFullTextColor textColor:UIColor,
                                 withChangeTexts changeTexts:[String],
                                 withChangeFont changFont:UIFont,
                                 withChangeColors changeColors:[UIColor],
                                 isLineThrough:Bool = false) -> NSAttributedString{
        
        var range:NSRange?
        var dicAttr:[NSAttributedString.Key:Any]?
        let attributeString = NSMutableAttributedString.init(string: strFullText)
        
        //不需要改变的文本
        range = NSString.init(string: strFullText).range(of: String.init(strFullText))
        
        dicAttr = [
            NSAttributedString.Key.font:textFont,
            NSAttributedString.Key.foregroundColor:textColor,
        ]
        attributeString.addAttributes(dicAttr!, range: range!)
        
        //需要改变的文本
        var _i = 0
        for item in changeTexts {
            range = NSString.init(string: strFullText).range(of: item)
            
            dicAttr = [
                NSAttributedString.Key.font:changFont,
                NSAttributedString.Key.foregroundColor:changeColors.count > _i ? changeColors[_i] : changeColors.first,
            ]
            
            if isLineThrough {
                dicAttr?[NSAttributedString.Key.strikethroughStyle] = NSNumber.init(value: 1)
            }
            attributeString.addAttributes(dicAttr!, range: range!)
            
            _i += 1
        }
        
        return attributeString
    }
    
    /**!
     * 设置字体行间距和字间距
     * @para label UILabel
     * @para str String 文本信息
     * @para font UIFont 字体
     * @para color UIColor 字体样色
     * @para spacing CGFloat 行间距
     * @para margin CGFloat 字间距
     */
    func setLabelSpace(label:UILabel,
                       withValue str:String,
                       withFont font:UIFont,
                       andTextColor color:UIColor,
                       andLineSpacing spacing:CGFloat = 6,
                       andMargin margin:CGFloat = 1.5) {
        
        let paraStyle:NSMutableParagraphStyle = NSMutableParagraphStyle.init()
        paraStyle.lineBreakMode = .byWordWrapping
        paraStyle.lineSpacing = spacing //设置行间距
        paraStyle.hyphenationFactor = 1.0
        paraStyle.firstLineHeadIndent = 0.0
        paraStyle.paragraphSpacingBefore = 0.0
        
        paraStyle.headIndent = 0
        paraStyle.tailIndent = 0
        
        //设置字间距
        let dic:[NSAttributedString.Key:Any] = [
            NSAttributedString.Key.font:font,
            NSAttributedString.Key.foregroundColor:color,
            NSAttributedString.Key.paragraphStyle:paraStyle,
            NSAttributedString.Key.kern:margin
        ]
        
        let attributeStr:NSAttributedString = NSAttributedString.init(string: str, attributes: dic)
        label.attributedText = attributeStr
    }
    
    /// 设置 placeholder 富文本
    /// - Parameter strFullText: 全部文本
    /// - Parameter changeText: 改变的文本
    /// - Parameter changFont: 字体
    /// - Parameter changeColor: 颜色
    /// - Parameter isLineThrough: 下划线
    func setPlaceholderAttributeString(ForFullText strFullText:String,
                                       ChangeText changeText:String,
                                       withChangeFont changFont:UIFont,
                                       withChangeColor changeColor:UIColor?,
                                       isLineThrough:Bool = false) -> NSAttributedString{
        
        var dicAttr:[NSAttributedString.Key:Any]?
        let attributeString = NSMutableAttributedString.init(string: strFullText)
        
        //需要改变的文本
        let range = NSString.init(string: strFullText).range(of: changeText)
        
        if changeColor != nil {
            dicAttr = [
                NSAttributedString.Key.font:changFont,
                NSAttributedString.Key.foregroundColor:changeColor!,
            ]
        }
        else{
            dicAttr = [
                NSAttributedString.Key.font:changFont,
            ]
        }
        
        if isLineThrough {
            dicAttr?[NSAttributedString.Key.strikethroughStyle] = NSNumber.init(value: 1)
        }
        attributeString.addAttributes(dicAttr!, range: range)
        
        return attributeString
    }
    
    
    /// 批量设置富文本
    /// - Parameter strFullText: 全部文本
    /// - Parameter textFont: 默认文本字体
    /// - Parameter textColor: 默认文本颜色
    /// - Parameter changeTexts: 需要改变的文本
    /// - Parameter changFonts: 需要改变的字体
    /// - Parameter changeColors: 需要改变的颜色
    /// - Parameter isLineThrough: 是否下划线
    func setAttributeStringTexts(ForFullText strFullText:String,
                                 FullTextFont textFont:UIFont,
                                 andFullTextColor textColor:UIColor,
                                 withChangeTexts changeTexts:[String],
                                 withChangeFonts changFonts:[UIFont],
                                 withChangeColors changeColors:[UIColor],
                                 isLineThrough:Bool = false) -> NSAttributedString{
        
        var range:NSRange?
        var dicAttr:[NSAttributedString.Key:Any]?
        let attributeString = NSMutableAttributedString.init(string: strFullText)
        
        //不需要改变的文本
        range = NSString.init(string: strFullText).range(of: String.init(strFullText))
        
        dicAttr = [
            NSAttributedString.Key.font:textFont,
            NSAttributedString.Key.foregroundColor:textColor,
        ]
        attributeString.addAttributes(dicAttr!, range: range!)
        
        //需要改变的文本
        var index:Int = 0
        for item in changeTexts {
            range = NSString.init(string: strFullText).range(of: item)
            
            dicAttr = [
                NSAttributedString.Key.font:changFonts.count > index ? changFonts[index] : changFonts.first as Any,
                NSAttributedString.Key.foregroundColor:changeColors.count > index ? changeColors[index] : changeColors.first as Any,
            ]
            
            if isLineThrough {
                dicAttr?[NSAttributedString.Key.strikethroughStyle] = NSNumber.init(value: 1)
            }
            attributeString.addAttributes(dicAttr!, range: range!)
            
            index += 1
        }
        
        return attributeString
    }
    
    
    /** 设置信息，检测是否有有手机号。有则添加拨打电话事件 */
    func setTextInfoToPhone(labInfo:YYLabel,
                            WithText text:String){
        
        let stringRange:NSRange = NSRange.init(location: 0, length: text.count)
        
        //匹配号码
        let strRegex = "\\d{3,4}[-]?\\d{7,8}"
        
        let regexps:NSRegularExpression? = try! NSRegularExpression.init(pattern: strRegex, options: NSRegularExpression.Options(rawValue: 0))
        if regexps != nil {
            let attributeStriung:NSMutableAttributedString = NSMutableAttributedString.init(string: text)
            attributeStriung.addAttributes([
                NSAttributedString.Key.font : labInfo.font as Any,
                NSAttributedString.Key.foregroundColor : labInfo.textColor as Any,
            ],
                                           range: stringRange)
            labInfo.attributedText = attributeStriung
            
            regexps?.enumerateMatches(in: text,
                                      options: NSRegularExpression.MatchingOptions.init(rawValue: 0),
                                      range: stringRange,
                                      using: { (result:NSTextCheckingResult?, flags:NSRegularExpression.MatchingFlags, _) in
                
                //可能为电话号码的字符串及其所在位置
                let phoneRange:NSRange = (result?.range)!
                let _phone = NSString.init(string: text).substring(with: phoneRange)
                
                attributeStriung.setTextHighlight(phoneRange,
                                                  color: UIColor.init().colorFromHexInt(hex: 0xFE9400), backgroundColor: UIColor.clear) { (_v:UIView, attr:NSAttributedString, _rang:NSRange, _rect:CGRect) in
                    Utils.shareInstance().showBoxFor(ViewController: UIApplication.shared.keyWindow!.rootViewController!,
                                                     andMessage: "是否拨打快递员电话",
                                                     andOK: "确定",
                                                     andOKBlock: { (action:UIAlertAction) in
                        let strTemp = String.init(format: "tel://%@", _phone)
                        if #available(iOS 10,*) {
                            UIApplication.shared.open(URL.init(string: strTemp)!, options: [:], completionHandler: { (result:Bool) in
                                print(result)
                            })
                        }
                        else{
                            UIApplication.shared.openURL(URL.init(string: strTemp)!)
                        }
                    },
                                                     andCancle: "取消")
                }
                labInfo.attributedText = attributeStriung
            })
        }
        else{
            labInfo.text = text
        }
    }
    
    
    //MARK: - 获取文本的宽度
    /**!
     * 获取文本的宽度
     */
    func getWidthForString(strTitle:String,txtFont:UIFont) -> CGFloat {
        var fw = CGFloat.init(0)
        let size = NSString.init(string: strTitle).size(withAttributes: [
            NSAttributedString.Key.font:txtFont
        ])
        fw = size.width
        
        return fw
    }
    
    /**!
     * 获取文本框的高度
     */
    func getTextViewHeight(textView: UITextView, fixedWidth:CGFloat) -> CGFloat {
        
        var fw = CGFloat.init(0)
        
        let size = CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)
        let constraint = textView.sizeThatFits(size)
        fw = constraint.height + 10
        
        return fw
    }
    
    
    //MARK: - 获取文本的高度
    /**!
     * 获取文本的高度
     */
    func getLabelHeight(text:String?,
                        withFont font:UIFont,
                        andWidth width:CGFloat) -> CGFloat {
        
        var _txt:String = ""
        if text != nil {
            _txt = text!
        }
        let size = NSString.init(string: _txt).boundingRect(with: CGSize.init(width: width, height: CGFloat.greatestFiniteMagnitude),
                                                            options: [.usesFontLeading,.usesLineFragmentOrigin,.truncatesLastVisibleLine],
                                                            attributes: [NSAttributedString.Key.font : font],
                                                            context: nil)
        
        return size.height
    }
    
    
    //MARK: - UISearchBar相关设置
    /**!
     * 清除查询框背景
     */
    func clearnSearchBarBg(searchView:UISearchBar){
        
        for item:UIView in (searchView.subviews) {
            for subview:UIView in item.subviews {
                if subview.isKind(of: UITextField.classForCoder()) {
                    
                    //将搜索文本框的背景去掉
                    for obj in subview.subviews {
                        obj.removeFromSuperview()
                    }
                }
            }
        }
    }
    
    /**!
     * 设置取消按钮为中文
     */
    func setSearchBarCancle(searchView:UISearchBar){
        
        for item:UIView in (searchView.subviews) {
            for subview:UIView in item.subviews {
                
                //更改取消按钮
                if subview.isKind(of:UIButton.classForCoder()) {
                    let btnCancle:UIButton = subview as! UIButton
                    
                    //修改文本
                    btnCancle.setTitle("取消", for: .normal)
                    
                    //修改背景
                    btnCancle.backgroundColor = UIColor.clear
                }
            }
        }
    }
    
    /**!
     * 设置提示文本字体大小
     */
    func setSearchBarFont(searchView:UISearchBar,font:UIFont){
        
        for item:UIView in (searchView.subviews) {
            for subview:UIView in item.subviews {
                if subview.isKind(of: UITextField.classForCoder()) {
                    (subview as? UITextField)?.font = font
                }
            }
        }
    }
    
    
    //MARK: - 相关App是否有安装检测
    //https://www.zhihu.com/question/19907735
    /**! 是否有安装QQ */
    func isInstallForQQ() -> Bool {
        //QQ mqq:// 或 mqqiapi://
        if !UIApplication.shared.canOpenURL(URL.init(string: "mqq://")!) &&
            !UIApplication.shared.canOpenURL(URL.init(string: "mqqiapi://")!) {
            return false
        }
        
        return true
    }
    
    /**! 是否有安装微信 */
    func isInstallForWechat() -> Bool {
        //微信 weixin:// 或 wechat://
        if !UIApplication.shared.canOpenURL(URL.init(string: "weixin://")!) &&
            !UIApplication.shared.canOpenURL(URL.init(string: "wechat://")!) {
            return false
        }
        
        return true
    }
    
    /**! 是否有安装新浪微博 */
    func isInstallForSina() -> Bool {
        //新浪微博 weibo:// 或 sinaweibo://
        if !UIApplication.shared.canOpenURL(URL.init(string: "weibo://")!) &&
            !UIApplication.shared.canOpenURL(URL.init(string: "sinaweibo://")!) {
            return false
        }
        
        return true
    }
    
    /**! 是否有安装支付宝 */
    func isInstallForAliPay() -> Bool {
        //支付宝 alipay://
        if !UIApplication.shared.canOpenURL(URL.init(string: "alipay://")!) {
            return false
        }
        
        return true
    }
    
    
    //MARK: - 视图设置
    /**!
     * 设置视图阴影
     *
     * @para uView        UIView  目标视图
     * @para shadowColor  UIColor 阴影颜色
     * @para shadowOpacity Float  阴影透明度(0.0 ~ 1.0,默认0.5)
     * @para shadowRadius CGFloat 阴影半径(默认3)
     * @para shadowOffset CGSize  阴影偏移，x向右偏移，y向下偏移，默认(0, -3)
     */
    func setViewShadowStyle(uView:UIView,
                            shadowColor:UIColor = Setting.K_APP_VIEW_SHADOW_COLOR,
                            shadowOpacity:Float = Setting.K_APP_VIEW_CELL_SHADOW_OPACITY,
                            shadowRadius:CGFloat = 3,
                            shadowOffset:CGSize = CGSize.init(width: 0, height: -3)){
        
        //shadowColor阴影颜色
        uView.layer.shadowColor = shadowColor.cgColor
        
        //阴影透明度
        uView.layer.shadowOpacity = shadowOpacity
        
        //阴影半径，默认3
        uView.layer.shadowRadius = shadowRadius
        
        //shadowOffset阴影偏移,
        //x向右偏移(正值)，y向下偏移(正值)，默认(0, -3),这个跟shadowRadius配合使用
        uView.layer.shadowOffset = shadowOffset
        
        //设置阴影此属性要设置为 false(masksToBounds 会造成离屏渲染)
        //uView.layer.masksToBounds = false
        var path:UIBezierPath = UIBezierPath.init(rect: uView.bounds)
        if uView.layer.cornerRadius > 0 {
            //存在圆角
            path = UIBezierPath.init(roundedRect: uView.bounds,
                                     cornerRadius: uView.layer.cornerRadius)
        }
        uView.layer.shadowPath = path.cgPath
    }
    
    
    
    /// 设置子视图阴影
    /// - Parameters:
    ///   - rect: CGRect
    ///   - dc: ShadowDirection
    /// - Returns: UIView
    func setChildViewShadowFor(CGRect rect:CGRect,
                               andDirecttion dc:ShadowDirection) -> UIView {
        
        let _child_v = BaseView.createView(rect: rect,
                                           backgroundColor: UIColor.clear,
                                           isRadius: false,
                                           cornerRadius: 0,
                                           andBorder: 0,
                                           andBorderColor: nil)
        
        var _sd_szie:CGSize = CGSize.init(width: 0, height: 0)
        switch dc {
        case .left:
            _sd_szie = CGSize.init(width: -2, height: 0)
            
        case .bottom:
            _sd_szie = CGSize.init(width: 0, height: 2)
            
        case .top:
            _sd_szie = CGSize.init(width: 0, height: -2)
            
        case .right:
            _sd_szie = CGSize.init(width: 2, height: 0)
        }
        
        Utils.shareInstance().setViewShadowStyle(uView: _child_v,
                                                 shadowRadius: 2,
                                                 shadowOffset: _sd_szie)
        
        return _child_v
    }
    
    
    /// 设置视图渐变背景色
    /// - Parameters:
    ///   - parentView: 渐变视图
    ///   - colors: [CGColor]
    ///   - type: 1上线渐变 2对角渐变
    func setColorlayer(parentView:UIView,
                       WithColors colors:[CGColor],
                       andType type:Int = 1) {
        
        let gradient:CAGradientLayer = CAGradientLayer.init()
        gradient.frame = parentView.bounds
        
        //colors = @[
        //(__bridge id)[UIColor colorWithRed:49.f/255.f green:240.f/255.f blue:233.f/255.f alpha:1.0].CGColor,
        //(__bridge id)[UIColor colorWithRed:69.f/255.f green:240.f/255.f blue:210.f/255.f alpha:1.0].CGColor,
        //];
        gradient.colors = colors
        
        gradient.locations = [NSNumber.init(value: 0.15),NSNumber.init(value: 0.85)]
        
        //起始位置
        gradient.startPoint = CGPoint.zero;
        //结束位置
        gradient.endPoint = CGPoint.init(x: 0, y: 1)
        if type == 2 {
            gradient.endPoint = CGPoint.init(x: 1, y: 1)
        }
        
        //清除边框
        gradient.masksToBounds = true
        gradient.borderWidth = 0.0
        gradient.borderColor = UIColor.clear.cgColor
        
        parentView.layer.addSublayer(gradient)
    }
    
    
    //MARK: - 销毁
    func gotoRoot(ViewController vcs:[UIViewController]?) {
        if vcs != nil {
            for vc in vcs! {
                
                vc.view.superview?.removeAssociatedValues()
                vc.view.superview?.removeAllSubviews()
                vc.view.removeAllSubviews()
                
                vc.view.removeObserverBlocks()
                vc.view.removeAssociatedValues()
                vc.view.removeFromSuperview()
                vc.removeFromParent()
                
                //内存警告处理
                vc.removeAssociatedValues()
                vc.removeObserverBlocks()
                
                //移除
                vc.dismiss(animated: false, completion: nil)
                vc.navigationController?.popViewController(animated: false)
            }
        }
    }
    
    
    //MARK: - 屏幕处理
    /// 强制横屏
    func screenDirection(){
        let _value = UIInterfaceOrientation.landscapeLeft.rawValue
        let _selector = #selector(NSObject.setValue(_:forKey:))
        if UIDevice.current.responds(to: _selector) {
            UIDevice.current.setValue(_value, forKey: "orientation")
        }
    }
    
    
    /// 默认竖屏
    func defaultDirection(){
        let _value = UIInterfaceOrientation.portrait.rawValue
        let _selector = #selector(NSObject.setValue(_:forKey:))
        if UIDevice.current.responds(to: _selector) {
            UIDevice.current.setValue(_value, forKey: "orientation")
        }
    }
    
    
    //MARK: - 提示信息
    /// 交互式提示弹框1
    /// - Parameters:
    ///   - vc: 当前的 ViewController
    ///   - m: 消息
    ///   - _ok: 确定标题
    ///   - imp:是否重要(重要 红色，否则默认主题色)
    ///   - _b: 确定回调
    ///   - c: 取消标题
    ///   - t: 弹框类型
    func showBoxFor(ViewController vc:UIViewController,
                    andMessage _m:String,
                    andOK _ok:String?,
                    andOKImport _imp:Bool = false,
                    andOKBlock _b:((_ action:UIAlertAction)->())?,
                    andCancle _c:String?,
                    withType t: UIAlertController.Style = .alert,
                    withInfo info:String = "",
                    andTargetView tv:UIView? = nil) {
        
        autoreleasepool {
            let alert = UIAlertController.init(title: _m,
                                               message: info, preferredStyle: t)
            if UIDevice.current.isPad {
                alert.popoverPresentationController?.sourceView = tv != nil ? tv : UIApplication.shared.keyWindow?.viewController!.view
                alert.popoverPresentationController?.sourceRect = (tv != nil ? tv!.bounds : UIApplication.shared.keyWindow?.viewController!.view.bounds)!
            }
            
            if _b != nil {
                let ok = UIAlertAction.init(title: _ok,
                                            style: _imp ? UIAlertAction.Style.destructive : UIAlertAction.Style.default) {(alertAction:UIAlertAction) in
                    _b!(alertAction)
                }
                alert.addAction(ok)
            }
            
            if _c != nil && _c! != "" {
                let cancleAction = UIAlertAction.init(title: _c!, style: UIAlertAction.Style.cancel, handler: nil)
                cancleAction.setValue(UIColor.init().colorFromHexString(hex: "#999999"), forKey: "titleTextColor")
                alert.addAction(cancleAction)
            }
            
            vc.present(alert, animated: true, completion: nil)
        }
    }
    
    
    /// 交互式提示弹框2
    /// - Parameters:
    ///   - vc: vc description
    ///   - _m: 消息
    ///   - _ok: 确定标题
    ///   - _imp: 是否重要(重要 红色，否则默认主题色)
    ///   - _ob: 确定回调
    ///   - _c: 取消标题
    ///   - _cb: 取消回调
    ///   - t: 弹框类型
    func showBoxFor(ViewController vc:UIViewController,
                    andMessage _m:String,
                    andOK _ok:String?,
                    andOKImport _imp:Bool = false,
                    andOKBlock _ob:(()->())?,
                    andCancle _c:String?,
                    andCancleBlock _cb:(()->())?,
                    withType t: UIAlertController.Style = .alert,
                    andTargetView tv:UIView? = nil) {
        
        autoreleasepool {
            let alert = UIAlertController.init(title: _m,
                                               message: "", preferredStyle: t)
            if UIDevice.current.isPad {
                alert.popoverPresentationController?.sourceView = tv != nil ? tv : UIApplication.shared.keyWindow?.viewController!.view
                alert.popoverPresentationController?.sourceRect = (tv != nil ? tv!.bounds : UIApplication.shared.keyWindow?.viewController!.view.bounds)!
            }
            
            if _cb != nil {
                let ok = UIAlertAction.init(title: _ok,
                                            style: _imp ? UIAlertAction.Style.destructive : UIAlertAction.Style.default) {(alertAction:UIAlertAction) in
                    _ob?()
                }
                alert.addAction(ok)
            }
            
            if _c != nil && _c! != "" {
                
                let cancleAction = UIAlertAction.init(title: _c!, style: UIAlertAction.Style.cancel, handler: { (alertAction:UIAlertAction) in
                    _cb?()
                })
                cancleAction.setValue(UIColor.init().colorFromHexString(hex: "#999999"), forKey: "titleTextColor")
                alert.addAction(cancleAction)
            }
            
            vc.present(alert, animated: true, completion: nil)
        }
        
    }
    
    
    /// 交互弹框
    /// - Parameters:
    ///   - _vc: 当前视图
    ///   - _arr: 显示自定义信息
    ///   - _title: 标题
    ///   - _ok: 确定标题
    ///   - imp: 重要性
    ///   - _b: 确定回调
    ///   - c: 取消标题
    ///   - t: 样式
    func showBoxForConfirm(ViewController _vc:UIViewController,
                           andArrMsg _arr:[String],
                           andTitle _title:String,
                           andOK _ok:String?,
                           andOKImport imp:Bool = false,
                           andOKBlock _b:(()->(Void))?,
                           andCancle c:String?,
                           withType t: UIAlertController.Style = .alert,
                           withCancleBlock _cb:(()->(Void))? = nil,
                           andTargetView tv:UIView? = nil) {
        
        var _m = ""
        for _ in 0..<_arr.count {
            _m += "\n\n"
        }
        
        let alert = UIAlertController.init(title: _title,
                                           message:_m, preferredStyle: t)
        if UIDevice.current.isPad {
            alert.popoverPresentationController?.sourceView = tv != nil ? tv : UIApplication.shared.keyWindow?.viewController!.view
            alert.popoverPresentationController?.sourceRect = (tv != nil ? tv!.bounds : UIApplication.shared.keyWindow?.viewController!.view.bounds)!
        }
        
        //[S] 添加子视图
        var _labInfo:YYLabel?
        for index in 0..<_arr.count {
            
            let rect = CGRect.init(x: 20, y: 45 + index * 21 + (index + 1) * 10, width: UIDevice.current.isSmallDevice() ? 265 : 340, height: 21)
            _labInfo = YYLabel.init(frame: rect)
            _labInfo?.textColor = UIColor.init().colorFromHexString(hex: "#333333")
            _labInfo?.font = UIFont.systemFont(ofSize: 15, weight: .bold)
            _labInfo?.textAlignment = .left
            _labInfo?.numberOfLines = 0
            
            _labInfo?.text = _arr[index]
            alert.view.addSubview(_labInfo!)
        }
        //[E] 添加子视图
        
        if _b != nil {
            let ok = UIAlertAction.init(title: _ok,
                                        style: imp ? UIAlertAction.Style.destructive : UIAlertAction.Style.default) {(alertAction:UIAlertAction) in
                _b!()
            }
            alert.addAction(ok)
        }
        
        if c != nil {
            let cancleAction = UIAlertAction.init(title: c!, style: UIAlertAction.Style.cancel) { (action:UIAlertAction) in
                _cb?()
            }
            cancleAction.setValue(UIColor.init().colorFromHexString(hex: "#999999"), forKey: "titleTextColor")
            alert.addAction(cancleAction)
        }
        
        _vc.present(alert, animated: true, completion: nil)
    }
    
    
    
    //MARK: - 获取汉字首字母
    /**!
     * 获取汉字首字母
     */
    func getLetter(strInput:String?) -> String {
        
        if strInput != nil && strInput != "" {
            var ms:NSMutableString? = NSMutableString.init(string: strInput!)
            
            CFStringTransform(ms, UnsafeMutablePointer.init(bitPattern: 0), kCFStringTransformMandarinLatin, false)
            CFStringTransform(ms, UnsafeMutablePointer.init(bitPattern: 0), kCFStringTransformStripDiacritics, false)
            
            var pyArr:[String]? = ms?.components(separatedBy: " ")
            if pyArr != nil && (pyArr?.count)! > 0 {
                var strResult:String = String()
                
                for item in pyArr! {
                    strResult += (item as NSString).substring(to: 1)
                }
                
                return strResult.uppercased()
            }
            
            ms = nil
            pyArr = nil
        }
        
        return strInput!
    }
    
    /**!
     * 获取第一个首字母
     */
    func getFirstLetter(strInput:String?) -> String {
        
        if strInput != nil && strInput != "" {
            var ms:NSMutableString? = NSMutableString.init(string: strInput!)
            
            CFStringTransform(ms, UnsafeMutablePointer.init(bitPattern: 0), kCFStringTransformMandarinLatin, false)
            CFStringTransform(ms, UnsafeMutablePointer.init(bitPattern: 0), kCFStringTransformStripDiacritics, false)
            
            var pyArr:[String]? = ms?.components(separatedBy: " ")
            if pyArr != nil && (pyArr?.count)! > 0 {
                var strResult:String = String()
                strResult = NSString.init(string: pyArr!.first!).substring(to: 1)
                
                return strResult.uppercased()
            }
            
            ms = nil
            pyArr = nil
        }
        
        return strInput!
    }
    
    
    //MARK: - Tabbar相关设置
    /**!
     * 设置tabBar的尺寸
     */
    func setTabbarHeight(tabBar:UITabBar){
        var rectTabbar = tabBar.frame
        
        if rectTabbar.size.height >= 49 {
            rectTabbar.size.height = Setting.K_APP_TABBAR_HEIGHT
            
            let statusBarFrame:CGRect = UIApplication.shared.statusBarFrame
            let statusBarHeight:CGFloat = statusBarFrame.size.height
            if (statusBarHeight > 20.0 && !UIDevice.current.isiPhoneX()) || ((statusBarHeight > 44.0 && UIDevice.current.isiPhoneX())) {
                rectTabbar.origin.y = Setting.K_APP_HEIGHT - Setting.K_APP_TABBAR_HEIGHT - 20
            }
            else{
                rectTabbar.origin.y = Setting.K_APP_HEIGHT - Setting.K_APP_TABBAR_HEIGHT
            }
            
            tabBar.frame = rectTabbar
        }
    }
    
    /**!
     * 设置tabBar的尺寸 顶部边框线
     */
    func setTopLine(tabBar:UITabBar,
                    lineColor:UIColor = UIColor.init().colorFromHexInt(hex: 0xdfdfdf)){
        // [S] 顶部边框线
        var topLineData:Data? = UserDefaults.standard.object(forKey: Setting.K_APP_TABBAR_TOP_LINE) as? Data
        if topLineData == nil || (topLineData?.count)! <= 0 {
            let rect = CGRect.init(x: 0, y: 0, width: Setting.K_APP_WIDTH, height: 0.1)
            UIGraphicsBeginImageContext(rect.size)
            
            let context:CGContext = UIGraphicsGetCurrentContext()!
            context.setFillColor(lineColor.cgColor)
            context.fill(rect)
            
            let imgTemp = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            
            topLineData = imgTemp!.pngData()
            UserDefaults.standard.set(topLineData, forKey: Setting.K_APP_TABBAR_TOP_LINE)
            UserDefaults.standard.synchronize()
        }
        tabBar.shadowImage = UIImage.init(data: topLineData!)
        // [E] 顶部边框线
    }
    
    
    /**!
     * 清除tabbar顶部边线
     */
    func clearnTabBarTopLine(tabBar:UITabBar) {
        
        var clearnLineData:Data? = UserDefaults.standard.object(forKey: Setting.K_APP_TABBAR_CLEARN_IMAGE) as? Data
        
        if clearnLineData == nil || clearnLineData!.count <= 0 {
            let rect:CGRect = CGRect.init(x: 0, y: 0, width: 1, height: 1)
            UIGraphicsBeginImageContext(rect.size)
            let context:CGContext = UIGraphicsGetCurrentContext()!
            context.setFillColor(UIColor.clear.cgColor)
            context.fill(rect)
            
            let theImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            
            clearnLineData = theImage.pngData()
            UserDefaults.standard.set(clearnLineData, forKey: Setting.K_APP_TABBAR_CLEARN_IMAGE)
            UserDefaults.standard.synchronize()
        }
        
        tabBar.backgroundImage = YYImage.init(data: clearnLineData!)
        tabBar.shadowImage = YYImage.init(data: clearnLineData!)
    }
    
    
    //MARK: - 图片相关处理
    /**! 图片不变形处理 */
    func imgNoTransformation(img:UIImageView) {
        img.contentMode = .scaleAspectFill
        img.clipsToBounds = true //是否剪切掉超出 UIImageView 范围的图片
        img.contentScaleFactor = UIScreen.main.scale
    }
    
    /**!
     * 截屏
     * @param view UIView  需要截屏的视图
     */
    func screenCaptureImage(view:UIView) -> UIImage {
        autoreleasepool {
            UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
            
            let context:CGContext = UIGraphicsGetCurrentContext()!
            //view.layer.render(in: context)
            view.layer.draw(in: context)
            view.layer.contents = nil //释放缓存
            
            let theImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            
            return theImage
        }
    }
    
    
    /// 截取全屏长图
    /// https://github.com/ZFBest/UITableViewSnapshot
    /// - Parameter sv: UIScrollView
    /// - Returns: UIImage
    func screenCaptureImageFor(UIScrollView sv:UIScrollView) -> UIImage {
        autoreleasepool {
            UIGraphicsBeginImageContextWithOptions(sv.contentSize, false, 0)
            
            //保存现在视图的位置偏移信息
            let saveContentOffset:CGPoint = sv.contentOffset
            
            //保存现在视图的frame信息
            let saveFrame:CGRect = sv.frame
            
            //设置要截图的视图的frame为内容尺寸大小
            sv.frame = .init(x: 0, y: 0, width: sv.contentSize.width, height: sv.contentSize.height)
            
            //把要截图的视图偏移量设置为0
            sv.contentOffset = .zero
            
            sv.drawHierarchy(in: sv.frame, afterScreenUpdates: true)
            
            //因为 renderInContext 渲染时会导致内存急剧上升,可能会造成crash, 所以要清除 layer 绘制过后产生的缓存
            sv.layer.contents = nil //释放缓存
            
            let theImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            
            //将视图的偏移量设置回原来的状态
            sv.contentOffset = saveContentOffset
            
            //将视图的frame信息设置回原来的状态
            sv.frame = saveFrame
            
            return theImage
        }
    }
    
    
    /// 图片压缩处理
    /// - Parameter sourceImage: UIImage
    func zipDataWith(Image sourceImage:UIImage) -> Data? {
        autoreleasepool {
            //进行图像尺寸的压缩
            let imageSize:CGSize = sourceImage.size//取出要压缩的image尺寸
            var width:CGFloat = imageSize.width    //图片宽度
            var height:CGFloat = imageSize.height  //图片高度
            
            //1.宽高大于1280(宽高比不按照2来算，按照1来算)
            if width > 1280 || height > 1280 {
                if width>height {
                    let scale:CGFloat = height/width
                    width = 1280
                    height = width * scale
                }
                else{
                    let scale:CGFloat  = width/height
                    height = 1280
                    width = height * scale
                }
            }
            //2.宽大于1280高小于1280
            else if width > 1280 || height < 1280 {
                let scale:CGFloat = height/width
                width = 1280
                height = width * scale
            }
            //3.宽小于1280高大于1280
            else if width < 1280 || height > 1280 {
                let scale:CGFloat = width / height
                height = 1280
                width = height * scale
            }
            //4.宽高都小于1280
            else{ }
            
            UIGraphicsBeginImageContext(CGSize.init(width: width, height: height))
            sourceImage.draw(in: CGRect.init(x: 0, y: 0, width: width, height: height))
            
            let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            
            //进行图像的画面质量压缩
            var data:Data? = newImage.jpegData(compressionQuality: 1.0)
            if data != nil && data!.count > 100*1024 {
                //1M以及以上
                if data!.count > 1024*1024 {
                    data = newImage.jpegData(compressionQuality: 0.7)
                }
                //0.5M-1M
                else if data!.count > 512 * 1024 {
                    data = newImage.jpegData(compressionQuality: 0.8)
                }
                //0.25M-0.5M
                else if data!.count > 200 * 1024 {
                    data = newImage.jpegData(compressionQuality: 0.9)
                }
            }
            
            return data
        }
    }
    
    
    //MARK: - 创建自定义相册
    func createPhotoAlbumFor(Name n:String,
                             andBlackAction _backAction:@escaping((_ _assetCollection:PHAssetCollection?) -> (Void))){
        
        if n == "" {
            SVProgressHUD.showInfo(withStatus: "相册名字不为空")
            _backAction(nil)
            return
        }
        
        self.isExistPhotoAlbumFor(Name: n) { (assetCollection:PHAssetCollection?) -> (Void) in
            if assetCollection != nil {
                _backAction(assetCollection)
            }
            else{
                //不存在
                PHPhotoLibrary.shared().performChanges({
                    //添加HUD文件夹
                    PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: n)
                }) { (success:Bool, error:Error?) in
                    if success {
                        print("创建相册文件夹成功!")
                        
                        self.isExistPhotoAlbumFor(Name: n) { (_phac:PHAssetCollection?) -> (Void) in
                            _backAction(_phac)
                        }
                    }
                    else {
                        print("创建相册文件夹失败:\(error.debugDescription)")
                        _backAction(nil)
                    }
                }
            }
        }
    }
    
    
    /// 检测相册是否存在
    /// - Parameters:
    ///   - n: 相册名
    ///   - andBlackAction: PHAssetCollection
    func isExistPhotoAlbumFor(Name n:String,
                              andBlackAction backAction:@escaping((_ assetCollection:PHAssetCollection?) -> (Void))) {
        
        var isExists = false
        
        //首先获取用户手动创建相册的集合
        let collectonResuts:PHFetchResult = PHCollectionList.fetchTopLevelUserCollections(with: nil)
        
        //对获取到集合进行遍历
        collectonResuts.enumerateObjects { (obj:PHCollection, idx:Int, stop:UnsafeMutablePointer<ObjCBool>) in
            
            guard let assetCollection = obj as? PHAssetCollection else {
                backAction(nil)
                return
            }
            
            //folderName是我们写入照片的相册
            if assetCollection.localizedTitle == n && n != "" {
                isExists = true
                
                backAction(assetCollection)
            }
        }
        
        if !isExists {
            backAction(nil)
        }
    }
    
}
